package com.lzw.breadknife.processor;

import com.lzw.breadknife.BindClick;
import com.lzw.breadknife.BindField;
import com.lzw.breadknife.BindLayout;
import com.lzw.breadknife.BindView;
import com.lzw.breadknife.ElementsHelper;
import com.lzw.breadknife.Constant;
import com.lzw.breadknife.ViewInjector;
import com.google.auto.service.AutoService;
import com.lzw.breadknife.util.Logger;
import com.lzw.breadknife.util.StringUtil;

import java.io.Writer;
import java.lang.annotation.Annotation;
import java.util.Set;

import javax.annotation.processing.Messager;
import javax.annotation.processing.Processor;
import javax.annotation.processing.RoundEnvironment;
import javax.annotation.processing.SupportedOptions;
import javax.lang.model.element.Element;
import javax.lang.model.element.TypeElement;
import javax.lang.model.element.VariableElement;
import javax.tools.JavaFileObject;

/**
 * author  : AWeiLoveAndroid
 * e-mail  : lzw460433675@163.com
 * github  : https://gitee.com/AWeiLoveAndroid/BreadKnife
 * project : BreadKnife
 */
@AutoService(Processor.class)
public class BreadKnifeProcessor extends AbstractBreadKnifeProcessor {

    boolean inited = false;
    String buidPath;
    String packages;

    @Override
    protected void processField(Messager messager, Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
        Set<? extends Element> elementsWithBind = roundEnv.getElementsAnnotatedWith(BindField.class);
        for (Element element : elementsWithBind) {
            if (!checkAnnotationValid(element, BindField.class)) continue;
            VariableElement variableElement = (VariableElement) element;
            TypeElement classElement = (TypeElement) variableElement.getEnclosingElement();
            String fullClassName = classElement.getQualifiedName().toString();
            ElementsHelper elementsHelper = mProxyMap.get(fullClassName);
            if (elementsHelper == null) {
                elementsHelper = new ElementsHelper(messager, elementUtils, classElement);
                mProxyMap.put(fullClassName, elementsHelper);
            }

        }
    }

    @Override
    protected void processClick(Messager messager, Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
        Set<? extends Element> elementsWithBind = roundEnv.getElementsAnnotatedWith(BindClick.class);
        for (Element element : elementsWithBind) {
            if (!checkAnnotationValid(element, BindClick.class)) continue;
            TypeElement typeElement = (TypeElement) element.getEnclosingElement();
            String fullClassName = typeElement.getQualifiedName().toString();
            ElementsHelper elementsHelper = mProxyMap.get(fullClassName);
            if (elementsHelper == null) {
                elementsHelper = new ElementsHelper(messager, elementUtils, typeElement);
                mProxyMap.put(fullClassName, elementsHelper);
            }
        }
    }

    @Override
    protected void processView(Messager messager, Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
        Set<? extends Element> elementsWithBind = roundEnv.getElementsAnnotatedWith(BindView.class);
        for (Element element : elementsWithBind) {
            if (!checkAnnotationValid(element, BindView.class)) continue;
            VariableElement variableElement = (VariableElement) element;
            TypeElement classElement = (TypeElement) variableElement.getEnclosingElement();
            String fullClassName = classElement.getQualifiedName().toString();
            ElementsHelper elementsHelper = mProxyMap.get(fullClassName);
            if (elementsHelper == null) {
                elementsHelper = new ElementsHelper(messager, elementUtils, classElement);
                mProxyMap.put(fullClassName, elementsHelper);
            }

        }
    }


    @Override
    protected void processLayout(Messager messager, Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
        Set<? extends Element> elementsWithBind = roundEnv.getElementsAnnotatedWith(BindLayout.class);
        for (Element element : elementsWithBind) {
            if (!checkAnnotationValid(element, BindLayout.class)) continue;
            String fullClassName = element.asType().toString();

            Annotation annotation = element.getAnnotation(BindLayout.class);
            if (annotation != null) {
                BindLayout myAnnotation = (BindLayout) annotation;
                String rootPackage = myAnnotation.rootPackage();
                int layoutId = myAnnotation.value();

                ElementsHelper elementsHelper = mProxyMap.get(fullClassName);
                if (elementsHelper == null) {
                    elementsHelper = new ElementsHelper(messager, elementUtils, (TypeElement) element);
                    elementsHelper.setRootPkg(rootPackage);
                    // fix: 添加了@BindLayout注解的类才使用代理类名，否则用原类名
                    elementsHelper.setIsShowProxyClassFullName(true);
                    mProxyMap.put(fullClassName, elementsHelper);
                }
            } else {
                return;
            }
        }
    }


    @Override
    protected void process(Messager messager) {
        for (String key : mProxyMap.keySet()) {
            ElementsHelper elementsHelper = mProxyMap.get(key);

            JavaFileObject fileObject = null;

            try {
                JavaFileObject jfo = processingEnv.getFiler().createSourceFile(
                        elementsHelper.getClassFullName(),
                        elementsHelper.getTypeElement());
                if (!inited) {
                    packages = elementsHelper.getPackageName(); // 这个是注解类所在的包名，不是项目真实的包名
                    try {
                        fileObject = processingEnv.getFiler().createSourceFile(packages + Constant.dot + Constant.breadKnifeInjector, elementsHelper.getTypeElement());
                        //找到当前module
                        buidPath = StringUtil.findBuildDir(fileObject.toUri().getPath());

                        //ViewInjector
                        ViewInjector injector = new ViewInjector();
//                        injector.writeCode(buidPath, packages);

                        // BreadKnifeInjector
                        Writer openWriter = fileObject.openWriter();
                        injector.writeCode(buidPath, packages, openWriter);

                        openWriter.flush();
                        openWriter.close();
                    } catch (Exception e) {
                        e.printStackTrace();
                    }

                    inited = true;
                }
                Writer writer = jfo.openWriter();
                writer.write(elementsHelper.generateJavaCode(buidPath, packages, processingEnv, elementsHelper.getTypeElement()));
                writer.flush();
                writer.close();
            } catch (Exception e) {
                Logger.logError(processingEnvironment, elementsHelper.getTypeElement(), "Unable to write injector for type: " + elementsHelper.getTypeElement());
                e.printStackTrace();
            }
        }

    }


}
